package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 服务端
 */
public class Server {
    //总机 ServerSocket
    //单 socket
    private ServerSocket serverSocket;//定义成员变量
    //创建一个集合，用于存储所有客户端的输出流
    //private List<PrintWriter> allOut=new ArrayList<>();
    private Map<String,PrintWriter> allOut =new HashMap<>();

    //构造方法
    public Server(){
        System.out.println("服务器启动中......");
        try {
            serverSocket =new ServerSocket(8088);
            System.out.println("服务器启动成功");
        } catch (IOException e) {
            e.printStackTrace();//控制台打印堆栈错误信息
        }

    }

    /*该方法用于启动程序的执行 */
    public void start(){
        try {
            while (true){
                System.out.println("等待客户上门：");
            /*
            accept()方法：用于接收客户端的连接请求
            该方法会一直阻塞，直到有客户端连接请求
             */
                Socket socket=serverSocket.accept();
                System.out.println("一个客户上门了");
                //创建一个线程
                //启动单独的线程来处理与客户端的通信
                Runnable r1=new ClientHandler(socket);
                Thread t1=new Thread(r1);
                t1.start();
            }
        } catch (IOException e) {
            e.printStackTrace();//控制台打印堆栈错误信息
        }
    }
    //主方法

    public static void main(String[] args) {
        Server server=new Server();
        server.start();
    }


    private class ClientHandler implements Runnable{
        private Socket socket;
        private String host;//记录客户端主机名
        private String nickName;//记录客户端的昵称
        public ClientHandler(Socket socket){
            this.socket=socket;
            //通过socket获取客户端的IP地址
            host=socket.getInetAddress().getHostAddress();
        }
        public void run(){
            PrintWriter pw=null;
            try {
                //获取输入流
                InputStream is=socket.getInputStream();
                InputStreamReader isr=new InputStreamReader(is, StandardCharsets.UTF_8);
                BufferedReader br=new BufferedReader(isr);
                //客户输入的第一句话,为用户的昵称
                //为该客户记录并设置昵称
                //System.out.println("测试这是不是第一条消息");
                nickName= br.readLine();
                //当客户端连接后,该线程其实是被卡在该处,即阻塞在该处
                /*String mess=br.readLine();
                System.out.println("测试这是不是第二条消息"+mess);*/

                //建立输出流,获取socket输出流,将消息发送至该客户端
                OutputStream os=socket.getOutputStream();
                OutputStreamWriter osw=new OutputStreamWriter(os,StandardCharsets.UTF_8);
                BufferedWriter bw=new BufferedWriter(osw);
                pw=new PrintWriter(bw,true);

                //将对应客户端的输出流添加到集合中
                //解决可能出现的并发安全问题,避免出现并发问题
                synchronized (allOut){
                    allOut.put(nickName,pw);
                }

                //广播该客户端消息
                broadcast(nickName+"["+host+"]"+"上线了,当前在线人数："+allOut.size());

                String message;
                while ((message=br.readLine())!=null){
                    /*System.out.println(host+"说："+Message);
                    //将消息发给客户端
                    for (PrintWriter o:allOut) {
                        o.println(host+"说：" + Message);
                    }*/

                    if (message.startsWith("@")){
                        //如果以"@"开始则为私聊功能
                        sendMessage(message);
                    }else {
                        //调用自定义broadcast方法
                        broadcast(nickName + host + "说：" + message);
                    }
                }
            } catch (IOException e) {
                //e.printStackTrace();//打印输出异常信息
            } finally {
                //处理该客户端断开连接的操作
                //1. 将客户端的输出流从集合中移除
                //  解决并发问题,且与add 的锁互斥
                synchronized (allOut){
                    allOut.remove(nickName);
                }

                //广播消息告知该客户端下线了
                broadcast(nickName+"["+host+"]"+"下线了,当前在线人数："+allOut.size());
                //关闭资源
                try {
                    socket.close();//四次挥手
                } catch (IOException e) {
                    e.printStackTrace();//打印输出异常信息
                }
            }
        }

        /*
        广播消息
         */
        private void broadcast(String message){

            System.out.println(message);
            //遍历集合,将消息发送给所有客户端
            //解决并发问题,且与add,和remove方法的锁互斥
            //在迭代器进行迭代便利时,集合不能进行增删操作,否则会报错

            synchronized (allOut) {
                for (PrintWriter o : allOut.values()) {
                    o.println(message);
                }
            }
        }
        public void sendMessage(String message){
            //验证消息的准确性
            if (message.matches("@.+:.+")) {

                //1. 获取私聊对象的昵称
                String toNickName = message.substring(1, message.indexOf(":"));
                //判断该昵称是否存在
                if (allOut.containsKey(toNickName)) {
                    //2. 获取私聊对象的输出流
                    PrintWriter pw = allOut.get(toNickName);
                    //3. 获取需要发生的消息
                    String toMessage = message.substring(message.indexOf(":") + 1);
                    //4. 发送消息
                    pw.println(nickName + "悄悄对你说：" + toMessage);
                }else {
                    PrintWriter pw=allOut.get(nickName);
                    pw.println("该用户不存在，请重新输入");
                }
           /* pw=allOut.get(nickName);
            pw.println(message);*/
            }else {
                //给当前用户提示私聊消息格式错误
                PrintWriter pw=allOut.get(nickName);
                pw.println("私聊消息格式错误!!!，请输入@昵称：私聊消息，请重新输入");
            }
        }


    }



}






















